package com.sinodata.bsm.cicp.util.telnet;

import java.io.IOException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import de.mud.telnet.TelnetWrapper;

/**
 * 利用 JTA 实现的telnet.
 * 
 * @author thomas.h.zhang
 */
public class JtaTelnet extends TelnetWrapper implements Telnet {
    private boolean isWindows = false;
    private final static Logger logger = Logger.getLogger(JtaTelnet.class);

    /**
     * 这是一个经验值, Shell 执行结果的每行长度不会大于256个字符. 但又不能分配得太小, 经实际测试 1024 为一个比较理想的尺寸.
     */

    private final int timeoutsecond = 17; // 10's

    // 标记此连接是否可用

    public JtaTelnet() {

    }

    private String shellPrompt;

    // 标记此连接是否可用
    private boolean isValid = true;
    private String HostIp = null;

    public String getHostIp() {
        return HostIp;
    }

    public void setHostIp(String hostIp) {
        HostIp = hostIp;
    }

    @Override
    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }

    @Override
    public boolean getIsValid() {
        return this.isValid;
    }

    /**
     * Set the prompt for the send() method.
     */

    @Override
    public void setPrompt(String prompt) {
        super.setPrompt(prompt);
        this.shellPrompt = prompt;
    }

    /**
     * Get the prompt.
     */

    @Override
    public String getPrompt() {
        return this.shellPrompt;
    }

    @Override
    public void login(String user, String pwd, String prompt) throws TelnetException {
        if (prompt.equals(">")) {
            isWindows = true;
        }
        try {
            login(user, pwd);
            setPrompt(prompt);
        } catch (IOException e) {
            logger.error("Login on " + this.HostIp + ":" + port + " " + user + "/" + prompt + " exception!", e);
            throw new TelnetException("Login on " + this.HostIp + ":" + port + " " + user + "/" + prompt + " exception!", e);
        }

        // 接收登录提示
        // Last login: Tue Jul 3 23:52:42 from 192.168.9.29
        // Sun Microsystems Inc. SunOS 5.10 Generic January 2005
        // this.runTask(null);
    }

    @Override
    public void login(String user, String pwd) throws IOException {

        String[] loginPrompt = new String[] { "ogin:", ":", "：" };
        String ff = waitfor2(loginPrompt); // throw output away
        // logger.info("loginusername[" + this.getHostIp() + "]:" + ff);
        send(user);
        try {
            Thread.sleep(600L);
        } catch (Exception ex) {
        }
        String[] pwdPrompt = new String[] { "assword:", ":", "：" };

        ff = waitfor2(pwdPrompt);
        // logger.info("loginpwd[" + this.getHostIp() + "]:" + ff);
        try {
            Thread.sleep(600L);
        } catch (Exception ex) {
        }
        send(pwd);
        send("");
    }

    public String waitfor2(String[] searchElements) throws IOException {
        byte[] b1 = new byte[1024];
        int n = 0;
        StringBuffer ret = new StringBuffer();
        String current;
        while (n >= 0) {
            n = read(b1);
            if (n > 0) {
                current = new String(b1, 0, n, "GBK");
                ret.append(current);
                for (int i = 0; i < searchElements.length; i++) {
                    if (StringUtils.endsWith(ret.toString().trim(), searchElements[i])) {
                        return ret.toString();
                    } // if
                } // for
            } // if
        } // while
        return null; // should never happen
    }

    @Override
    public String send(String cmd) throws IOException {
        byte arr[];
        if (isWindows) {
            arr = (cmd + "\r\n").getBytes();
        } else {
            arr = (cmd + "\n").getBytes();
        }
        handler.transpose(arr);
        if (getPrompt() != null)
            return waitfor(getPrompt());
        return null;
    }

    @Override
    public void sendCmd(String cmd) throws IOException {
        byte arr[];
        if (isWindows) {
            arr = (cmd + "\r\n").getBytes();
        } else {
            arr = (cmd + "\n").getBytes();
        }
        handler.transpose(arr);
    }

    /**
     * 正常情况: shell script: /usr/sbin/swapinfo -t shell out: /usr/sbin/swapinfo
     * -t Kb Kb Kb PCT START/ Kb TYPE AVAIL USED FREE USED LIMIT RESERVE PRI
     * NAME dev 4194304 250036 3944268 6% 0 - 1 /dev/vg00/lvol2 reserve -
     * 2774384 -2774384 memory 4182024 1243688 2938336 30% total 8376328 4268108
     * 4108220 51% - 0 - agentless$
     * 
     * 异常处理: shell script: UNIX95= ps -Ax -o pcpu,vsz,state,time,args | grep
     * ora_|grep -v grep shell out: UNIX95= ps -Ax -o pcpu,vsz,state,time,args |
     * grep ora _|grep -v gre agentless$ime,args | grep ora _|grep -v grep
     * < 0.02 131376 S 38:01 ora_ckpt_dlcj1 0.02
     * 147968 S 10:22 ora_smon_dlcj1 0.02 131376 S 27:24 ora_dbw0_dlcj1 0.05
     * 131120 S 1-03:18:26 ora_lms0_dlcj1 0.02 131376 S 05:11:07 ora_lgwr_dlcj1
     * 0.02 122928 S 11:11 ora_pmon_dlcj1 0.02 121280 S 00:18 ora_reco_dlcj1
     * 0.04 131360 S 02:05:44 ora_diag_dlcj1 0.02 123184 S 27:23 ora_dbw1_dlcj1
     * 0.02 121280 S 18:05 ora_cjq0_dlcj1 0.07 131120 S 1-03:26:32
     * ora_lms1_dlcj1 0.05 180272 S 21:34 ora_lck0_dlcj1 0.04 123184 S 02:50:03
     * ora_lmon_dlcj1 0.03 131120 S 02:53:48 ora_lmd0_dlcj1 agentless$
     */
    @Override
    public String recvResp() throws IOException {
        String rs = "";
        byte[] buf = new byte[4096];
        while (true) {
            int n = read(buf);
            if (n < 0) {
                throw new IOException("Recvice data from telnet server " + host + " error.");
            }
            String s = new String(buf, 0, n, "GBK");
            rs += s;
            if (isTermianl(s)) {
                return rs;
            }
        }
    }

    // 判断结尾处是否为提示符.
    // 结束符必须在末尾.
    private boolean isTermianl(String buf) {
        int promptLen = shellPrompt.length();
        int bufLen = buf.length();
        if (promptLen > bufLen) {
            return false;
        }
        if (buf.endsWith(" " + shellPrompt)) {
            return false;
        }
        String last = buf.substring(bufLen - promptLen, bufLen);
        if (shellPrompt.equals(last)) {
            return true;
        } else {
            // 末尾包含一个空格的提示符
            String spacePrompt = shellPrompt + " ";
            promptLen = spacePrompt.length();
            if (promptLen > bufLen) {
                return false;
            }
            last = buf.substring(bufLen - promptLen, bufLen);
            if (spacePrompt.equals(last)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void cancel() throws IOException {
        sendCmd("\003"); // ^C
        recvResp();
    }

    @Override
    public void runLogin(String user, String pwd, String prompt) throws TelnetException {
        LoginTask task = new LoginTask(this, user, pwd, prompt);
        task.start();

        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() || task.isActive()) {
            try {
                Thread.sleep(10);
                count++;
                if (count >= 7 * 100) {
                    Thread.interrupted();
                    task.stop();
                    logger.error("Login " + this.HostIp + " timeout: " + 7 + "s");
                    throw new TelnetTaskTimeoutException("Login timeout: " + 7 + "s");
                }
            } catch (InterruptedException e) {
            }
        }
    }

    @Override
    public synchronized String runTask(String shell) throws TelnetException {
        TelnetTask task = new TelnetTask(this, shell);
        task.start();
        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() || task.isActive()) {
            try {
                Thread.sleep(10);
                count++;
                if (count >= timeoutsecond * 100) {
                    try {
                        sendCmd("\003");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    Thread.interrupted();
                    task.stop();

                    throw new TelnetTaskTimeoutException("Execute " + this.HostIp + " command:[" + task.getCmd() + "] timeout:" + timeoutsecond + "s");
                }
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }

        String out = task.getShellOut();
        if (!StringUtils.startsWith(out, shell)) {
            CheckThread ct = new CheckThread(this, shell);
            ct.start();
            count = 0;
            while (!ct.isComplete()) {
                try {
                    Thread.sleep(10);
                    count++;
                    if (count >= 6 * 100) {
                        try {
                            sendCmd("\003");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        Thread.interrupted();
                        ct.stop();
                        this.setIsValid(false);
                        throw new TelnetTaskTimeoutException("Execute " + this.HostIp + " CheckThread:[" + ct.getCmd() + "] timeout:" + timeoutsecond + "s");
                    }
                } catch (InterruptedException e) {
                    logger.error(e);
                }
            }
            out = ct.getShellOut();
        }
        return out;

    }

    // public static void main(String[] args)
    // throws Exception
    // {
    // Telnet telnet = new JtaTelnet();
    //
    // String host = "122.122.12.33";
    // int port = 23;
    // String username = "ual";
    // String password = "agentless";
    // String prompt = "agentless$";
    // String cmd =
    // "UNIX95= ps -Ax -o pcpu,vsz,state,time,args|grep -v grep|grep 'ora_lms3_epmbj1'";
    // String recv;
    //
    // telnet.connect(host, port);
    // telnet.runLogin(username, password, prompt);
    //
    // // recv = telnet.runTask(null);
    // // System.out.println("shell out: [" + recv + "]");
    // // recv = telnet.waitfor("Terminal type?");
    // // System.out.println("waitfor: [" + recv + "]");
    //
    // recv = telnet.runTask(cmd);
    // // recv = telnet.waitfor(prompt);
    // // System.out.println("waitfor: [" + recv + "]");
    // System.out.println("shell out: [" + recv + "]");
    //
    // recv = telnet
    // .runTask("UNIX95= ps -Ax -o pcpu,vsz,state,time,args|grep -v grep|grep 'ora_lms2_epmbj1'");
    // System.out.println("shell out: [" + recv + "]");
    //
    // telnet
    // .sendCmd("UNIX95= ps -Ax -o pcpu,vsz,state,time,args|grep -v grep|grep 'ora_lms0_epmbj1'");
    // recv = telnet.recvResp();
    // System.out.println("shell out: [" + recv + "]");
    //
    // telnet.disconnect();
    // }

    public static void main(String[] args) throws Exception {
        JtaTelnet telnet = new JtaTelnet();

        String host = "172.168.2.210";
        int port = 23;
        String username = "root";
        String password = "itsm_1234";
        String prompt = "$";
        String cmd = "df -k";
        String recv;

        telnet.connect(host, port);
        telnet.runLogin(username, password, prompt);

        recv = telnet.runTask(cmd);
        // recv = telnet.waitfor(prompt);
        // System.out.println("waitfor: [" + recv + "]");
        System.out.println(recv);

        telnet.disconnect();
    }

}
